package com.ptone.messagecenter.common.handler;

import com.alibaba.fastjson.JSON;
import com.ptone.messagecenter.common.base.ErrCode;
import com.ptone.messagecenter.common.base.Result;
import com.ptone.messagecenter.common.utils.ExceptionUtils;
import com.ptone.messagecenter.common.utils.HttpContextUtils;
import com.ptone.messagecenter.common.utils.IpUtils;
import com.ptone.messagecenter.modules.msg.errlog.entity.LogError;
import com.ptone.messagecenter.modules.msg.errlog.service.LogErrorService;
import org.apache.catalina.connector.ClientAbortException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.json.JsonParseException;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MultipartException;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 错误信息拦截器
 */
@Configuration
@RestControllerAdvice
public class MyExceptionHandler {

    @Autowired
    private LogErrorService errLogService;
    @Autowired
    private HttpServletRequest request;

    private final Logger LOGGER = LoggerFactory.getLogger(getClass());

    /**
     * 不支持的请求方式
     *
     * @param e
     * @return
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result handleException(HttpRequestMethodNotSupportedException e) {
        saveLog(e);
        return Result.fail(ErrCode.REQUEST_TYPE_NO_SUPPORT, e.getMethod());
    }

    /**
     * 不支持的传参方式
     *
     * @param e
     * @return
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public Result handleException(HttpMediaTypeNotSupportedException e) {
        saveLog(e);
        return Result.fail(ErrCode.PARAMS_TYPE_MUST_BE_JSON, e);
    }

    /**
     * 参数非空或类型错误
     *
     * @param messageNotReadableException
     * @param missingServletRequestParameterException
     * @return
     */
    @ExceptionHandler({HttpMessageNotReadableException.class, MissingServletRequestParameterException.class})
    public Result handleException(HttpMessageNotReadableException messageNotReadableException, MissingServletRequestParameterException missingServletRequestParameterException) {
        Exception e = getException(messageNotReadableException, missingServletRequestParameterException);
        saveLog(e);
        return Result.fail(ErrCode.CHECK_PARAMS_FAIL, e);
    }

    /**
     * json转换异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(JsonParseException.class)
    public Result handleException(JsonParseException e) {
        saveLog(e);
        return Result.fail(ErrCode.JSON_ERROR, e);
    }

    /**
     * Valid 参数校验异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleException(MethodArgumentNotValidException e) {
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        Map<String, String> error = fieldErrors.stream()
                .collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));
        saveLog(e);
        return Result.fail(ErrCode.CHECK_PARAMS_FAIL, error);
    }

    /**
     * 参数验证异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(BindException.class)
    public Result handleException(BindException e) {
        BindingResult result = e.getBindingResult();
        Map<String, String> error = result.getAllErrors().stream()
                .collect(Collectors.toMap(x ->
                                Optional.ofNullable(x)
                                        .filter(y -> y instanceof FieldError)
                                        .map(y -> (FieldError) y)
                                        .map(FieldError::getField)
                                        .orElse(x.getObjectName()),
                        ObjectError::getDefaultMessage
                ));
        saveLog(e);
        return Result.fail(ErrCode.CHECK_PARAMS_FAIL, error);
    }

    /**
     * sql语句错误
     *
     * @param e
     * @return
     */
    @ExceptionHandler(BadSqlGrammarException.class)
    public Result handleException(BadSqlGrammarException e) {
        saveLog(e);
        return Result.fail(ErrCode.BAD_SQL_GRAMMAR, e);
    }

    /**
     * 浏览器关闭连接异常
     *
     * @param clientAbortException
     * @param multipartException
     * @return
     */
    @ExceptionHandler({ClientAbortException.class, MultipartException.class})
    public Result handleException(ClientAbortException clientAbortException, MultipartException multipartException) {
        Exception e = getException(clientAbortException, multipartException);
        saveLog(e);
        return Result.fail(ErrCode.BROWSER_CONNECTION_CLOSED, e);
    }

    /**
     * JSON化失败异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(HttpMessageNotWritableException.class)
    public Result handleException(HttpMessageNotWritableException e) {
        saveLog(e);
        return Result.fail(ErrCode.JSON_ERROR, e);
    }

    @ExceptionHandler({Exception.class})
    public Object handleExceptions(Exception e) {
        LOGGER.error("捕获未处理异常:  ", e);
        saveLog(e);
        return Result.fail(ErrCode.SYSTEM_ERROR, e);
    }

    /**
     * 多种exception时，用于找到非空的exception
     *
     * @param e
     * @return
     */
    private Exception getException(Exception... e) {
        List<Exception> exceptions = Arrays.asList(e);
        return exceptions.stream().filter(Objects::nonNull).findFirst().orElse(new Exception());
    }


    /**
     * 保存异常日志
     */
    private void saveLog(Exception ex) {
        LogError log = new LogError();
        //请求相关信息
        log.setIp(IpUtils.getIpAddr(request));
        log.setRequestMethod(request.getMethod());
        log.setRequestUri(request.getRequestURI());
        Map<String, String> params = HttpContextUtils.getParameterMap(request);
        if (!CollectionUtils.isEmpty(params))
            log.setRequestParams(JSON.toJSONString(params));
        //异常信息
        log.setErrorInfo(ExceptionUtils.getErrorStackTrace(ex));

        //保存
        errLogService.save(log);
    }
}